/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCovereds;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getInteractionFor;

import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;

import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.Lifeline;

import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.edit.analysis.CoverableAnalyser;
import com.cea.papyrus.diagram.sequence.sedi.edit.analysis.CoverableAnalyser.SetEntry;
import com.cea.papyrus.diagram.sequence.sedi.model.command.LifelinePosition;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Determines the position of a separator with respect to events on the covered lifelines.
 * A separator determines the boundary between two InteractionOperands, so we define
 * "position" as the index of the first event (InteractionFragments) that is to belong to
 * the lower operand. For more information, see "see also".
 * <p>
 * The following attributes must be set before the command can run successfully:
 * </p>
 * <ul>
 * <li>AnyEditPart
 * <li>InteractionOperand
 * <li>SplitY - the Y coordinate where the split should occurr. This is given explicitely
 * rather that derived from the separator because clients might want to test new positions
 * that is not yet formalized in the model.
 * </ul>
 * <p>
 * When completed, the following attributes can be read to access the result:
 * </p>
 * <ul>
 * <li>Positions
 * </ul>
 * 
 * @author frankd@ifi.uio.no
 * @see com.cea.papyrus.diagram.sequence.sedi.model.command.MoveSeparatorCommand
 */
public class AnalyseSeparatorPositionsCommand extends Command {

    private EditPart anyEditPart;
    private InteractionOperand operand;
    private int separatorY;
    private List<LifelinePosition> positions;

    public void setAnyEditPart(final EditPart anyEditPart) {
        this.anyEditPart = anyEditPart;
    }

    public void setInteractionOperand(final InteractionOperand operand) {
        this.operand = operand;
    }

    public void setSeparatorY(int y) {
        this.separatorY = y;
    }

    public List<LifelinePosition> getPositions() {
        return positions;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void execute() {
        // Find the bounds of the operand that will be split by the separator
        // For each covered lifeline:
        // Select the first "event" below the separator line
        final Rectangle operandBounds =
                OperandUtils.getOperandBounds(getDiagram(), operand, anyEditPart);

        CombinedFragment cfragment = (CombinedFragment) operand.getOwner();
        positions = new ArrayList<LifelinePosition>(getCovereds(cfragment).size());
        final CoverableAnalyser analyser = getCoverableAnalyser();
        for (final Lifeline lifeline : getCovereds(cfragment)) {
            List<GraphNode> listLifeline =
                    LookForElement.lookForGraphElementWODiagram(lifeline, getDiagram(),
                            new ArrayList<GraphNode>(1));
            final GraphNode lifelineView = listLifeline.get(0);
            final SortedSet<SetEntry> fragments = analyser.collectFragments(lifelineView);

            InteractionFragment first = null;
            for (final SetEntry entry : fragments) {
                if (entry.getCoordinate() > separatorY
                        && entry.getCoordinate() < operandBounds.bottom()) {
                    first = entry.getFragment();
                    break;
                }
            }

            final int firstIndex = lifeline.getCoveredBys().indexOf(first);
            positions.add(new LifelinePosition(lifeline, firstIndex));
        }
    }

    @Override
    public void undo() {
        positions = null;
    }

    @Override
    public void redo() {
        // does nothing.
    }

    @Override
    public void dispose() {
        operand = null;
        anyEditPart = null;
        positions = null;
    }

    @SuppressWarnings("unchecked")
    private CoverableAnalyser getCoverableAnalyser() {
        final Diagram diagram = getDiagram();
        final Interaction interaction = getInteractionFor(getCombinedFragment());
        List<GraphNode> listInter =
                LookForElement.lookForGraphElementWODiagram(interaction, diagram,
                        new ArrayList<GraphNode>(1));
        final GraphNode interactionView = listInter.get(0);
        return new CoverableAnalyser(anyEditPart, interactionView);
    }

    private Diagram getDiagram() {
        GraphElement m = (GraphElement) anyEditPart.getModel();
        return LookForElement.getDiagram(m);
    }

    private CombinedFragment getCombinedFragment() {
        return (CombinedFragment) operand.getOwner();
    }
}
